package com.stormragetech.teamwork.common.util;

import com.stormragetech.teamwork.common.constant.Constants;

import javax.crypto.Cipher;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

/**
 * description: //TODO
 *
 * @author: Xue Bin
 * @version: 1.0.0-SNAPSHOT
 */
public class RSACrypt {

    private static final String JAVA_ALGORITHM = "RSA/ECB/PKCS1Padding";
    private static final String ANDROID_ALGORITHM = "RSA/ECB/NoPadding";

    public static String sign(String data, String key) throws Exception {

        //生成RSA私钥
        //
        byte[] keyByte = Base64Coder.decode(key);
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyByte);
        KeyFactory factory = KeyFactory.getInstance(Constants.RSA_ALGORITHM);
        PrivateKey privateKey = factory.generatePrivate(spec);

        //生成sign签名
        //
        Signature signature = Signature.getInstance(Constants.SIGNATURE_ALGORITHM);
        signature.initSign(privateKey);
        signature.update(data.getBytes("UTF-8"));
        return Base64Coder.encode(signature.sign());

    }

    public static boolean verify(String data, String sign, String key) throws Exception {

        //生成RSA公钥
        //
        byte[] keyByte = Base64Coder.decode(key);
        X509EncodedKeySpec spec = new X509EncodedKeySpec(keyByte);
        KeyFactory factory = KeyFactory.getInstance(Constants.RSA_ALGORITHM);
        PublicKey publicKey = factory.generatePublic(spec);

        //验证签名
        //
        Signature signature = Signature.getInstance(Constants.SIGNATURE_ALGORITHM);
        signature.initVerify(publicKey);
        signature.update(data.getBytes(Constants.ENCODING));
        // 验证签名是否正常
        return signature.verify(Base64Coder.decode(sign));

    }


    /**
     * @Title: encryptByPrivateKey
     * @Description 私钥加密
     */
    public static String encryptByPrivateKey(String data, String key, Integer algorithm) throws Exception {

        //生成RSA秘钥
        byte[] keyByte = Base64Coder.decode(key);
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyByte);
        KeyFactory factory = KeyFactory.getInstance(Constants.RSA_ALGORITHM);
        RSAPrivateKey privateKey = (RSAPrivateKey) factory.generatePrivate(spec);

        Cipher cipher = Cipher.getInstance(getAlgorithm(algorithm));
        cipher.init(Cipher.ENCRYPT_MODE, privateKey);

        int kenLen = privateKey.getModulus().bitLength() / 8;
        String[] datas = splitStr(data, kenLen - 11);

        StringBuilder sb = new StringBuilder();
        for (String s : datas) {
            sb.append(bcdToStr(cipher.doFinal(s.getBytes())));
        }

        return sb.toString();

    }

    /**
     * @title: decryptByPrivateKey
     * @description 私钥解密
     */
    public static String decryptByPrivateKey(String data, String key, Integer algorithm) throws Exception {

        byte[] keyByte = Base64Coder.decode(key);
        PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyByte);
        KeyFactory factory = KeyFactory.getInstance(Constants.RSA_ALGORITHM);
        RSAPrivateKey privateKey = (RSAPrivateKey) factory.generatePrivate(spec);

        Cipher cipher = Cipher.getInstance(getAlgorithm(algorithm));
        cipher.init(Cipher.DECRYPT_MODE, privateKey);

        int keyLen = privateKey.getModulus().bitLength() / 8;
        byte[] bytes = data.getBytes();
        byte[][] arrays = splitArray(asciiToBcd(bytes, bytes.length), keyLen);

        StringBuilder sb = new StringBuilder();
        for (byte[] arr : arrays) {
            sb.append(new String(cipher.doFinal(arr)));
        }

        return sb.toString();

    }

    /**
     * @title: encryptByPublicKey
     * @description 公钥加密
     */
    public static String encryptByPublicKey(String data, String key, Integer algorithm) throws Exception {

        byte[] keyByte = Base64Coder.decode(key);
        X509EncodedKeySpec spec = new X509EncodedKeySpec(keyByte);
        KeyFactory factory = KeyFactory.getInstance(Constants.RSA_ALGORITHM);
        RSAPublicKey publicKey = (RSAPublicKey) factory.generatePublic(spec);

        Cipher cipher = Cipher.getInstance(getAlgorithm(algorithm));
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);

        int kenLen = publicKey.getModulus().bitLength() / 8;
        String[] datas = splitStr(data, kenLen - 11);

        StringBuilder sb = new StringBuilder();
        for (String s : datas) {
            sb.append(bcdToStr(cipher.doFinal(s.getBytes())));
        }

        return sb.toString();

    }

    /**
     * @title: decryptByPublicKey
     * @description 公钥解密
     */
    public static String decryptByPublicKey(String data, String key, Integer algorithm) throws Exception {

        byte[] keyByte = Base64Coder.decode(key);
        X509EncodedKeySpec spec = new X509EncodedKeySpec(keyByte);
        KeyFactory factory = KeyFactory.getInstance(Constants.RSA_ALGORITHM);
        RSAPublicKey publicKey = (RSAPublicKey) factory.generatePublic(spec);

        Cipher cipher = Cipher.getInstance(getAlgorithm(algorithm));
        cipher.init(Cipher.DECRYPT_MODE, publicKey);

        int keyLen = publicKey.getModulus().bitLength() / 8;
        byte[] bytes = data.getBytes();
        byte[][] arrays = splitArray(asciiToBcd(bytes, bytes.length), keyLen);

        StringBuilder sb = new StringBuilder();
        for (byte[] arr : arrays) {
            sb.append(new String(cipher.doFinal(arr)));
        }

        return sb.toString();


    }

    /**
     * @title asciiToBcd
     * @description ASCII码转BCD码
     */
    private static byte[] asciiToBcd(byte[] ascii, int ascLen) {
        byte[] bcd = new byte[ascLen / 2];
        int j = 0;
        for (int i = 0; i < (ascLen + 1) / 2; i++) {
            bcd[i] = ascToBcd(ascii[j++]);
            bcd[i] = (byte) (((j >= ascLen) ? 0x00 : ascToBcd(ascii[j++])) + (bcd[i] << 4));
        }
        return bcd;
    }

    private static byte ascToBcd(byte asc) {

        byte bcd;

        if ((asc >= '0') && (asc <= '9')) {
            bcd = (byte) (asc - '0');
        } else if ((asc >= 'A') && (asc <= 'F')) {
            bcd = (byte) (asc - 'A' + 10);
        } else if ((asc >= 'a') && (asc <= 'f')) {
            bcd = (byte) (asc - 'a' + 10);
        } else {
            bcd = (byte) (asc - 48);
        }

        return bcd;
    }

    /**
     * @title bcdToStr
     * @description BCD码转字符串
     */
    private static String bcdToStr(byte[] bytes) {
        char temp[] = new char[bytes.length * 2], val;

        for (int i = 0; i < bytes.length; i++) {
            val = (char) (((bytes[i] & 0xf0) >> 4) & 0x0f);
            temp[i * 2] = (char) (val > 9 ? val + 'A' - 10 : val + '0');

            val = (char) (bytes[i] & 0x0f);
            temp[i * 2 + 1] = (char) (val > 9 ? val + 'A' - 10 : val + '0');
        }
        return new String(temp);
    }

    /**
     * @title splitStr
     * @description 拆分字符串
     */
    private static String[] splitStr(String string, int len) {
        int x = string.length() / len;
        int y = string.length() % len;
        int z = 0;
        if (y != 0) {
            z = 1;
        }
        String[] strings = new String[x + z];
        String str;
        for (int i = 0; i < x + z; i++) {
            if (i == x + z - 1 && y != 0) {
                str = string.substring(i * len, i * len + y);
            } else {
                str = string.substring(i * len, i * len + len);
            }
            strings[i] = str;
        }
        return strings;

    }

    /**
     * @title splitArray
     * @description 拆分数组
     */
    private static byte[][] splitArray(byte[] data, int len) {
        int x = data.length / len;
        int y = data.length % len;
        int z = 0;
        if (y != 0) {
            z = 1;
        }
        byte[][] arrays = new byte[x + z][];
        byte[] arr;
        for (int i = 0; i < x + z; i++) {
            arr = new byte[len];
            if (i == x + z - 1 && y != 0) {
                System.arraycopy(data, i * len, arr, 0, y);
            } else {
                System.arraycopy(data, i * len, arr, 0, len);
            }
            arrays[i] = arr;
        }
        return arrays;
    }

    private static String getAlgorithm(Integer type) {

        String algorithm = null;

        if (Constants.JAVA_RSA.equals(type)) {
            algorithm = JAVA_ALGORITHM;
        }
        if (Constants.ANDROID_RSA.equals(type)) {
            algorithm = ANDROID_ALGORITHM;
        }

        return algorithm;

    }


}
